home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Snippets / Testing & Debugging / Audit / Src / Audit.c next >
Encoding:
C/C++ Source or Header  |  1993-07-21  |  31.5 KB  |  1,065 lines  |  [TEXT/MPS ]

  1. /*                                        Audit.c                                    */
  2. #define PARANOIA    0
  3. #include <Packages.h>
  4. /*
  5.  * Audit.c
  6.  * Copyright © 1992-93 Apple Computer Inc. All Rights Reserved.
  7.  * Programmed by Martin Minow,
  8.  *    Internet:    minow@apple.com
  9.  *    AppleLink:    MINOW
  10.  * Version of January 14, 1993
  11.  *
  12.  * Edit History
  13.  *    93.01.09 MM        First public release
  14.  *    93.01.14 MM        Think and MPW generate different record sizes; a disaster if
  15.  *                    you create an Audit Record under Think and call Audit compiled
  16.  *                    under MPW. Also added a test for record sizes and included
  17.  *                    record size information in the AuditRecord.
  18.  *    93.01.21 MM        Fix bug in procedure name recognizer that failed for certain
  19.  *                    pascal functions.
  20.  *    93.06.10 MM        Cosmetic edits for Think C 6.0. No functional changes. Text
  21.  *                    was reformatted to fit into an "80-column" line.
  22.  *
  23.  * This library implements a drop-in logging capability for device drivers,
  24.  * callback functions, applications, and all other Macintosh code segments.
  25.  *
  26.  * The overall philosophy is that an application or driver allocates a record in
  27.  * the system heap that contains a small preamble and one or more 48 (decimal)
  28.  * byte blocks of data. The driver logs data to the log area, where each log
  29.  * record contains an identification value and up to four longwords of data.
  30.  *
  31.  * The user would then write a log control application that can turn logging on or
  32.  * off and dump/format the data in the log. The user can also use a MacsBug dcmd
  33.  * to display the current state of the log area.
  34.  *
  35.  * To add logging to a driver, add the following variable to the driver control
  36.  * record:
  37.  *        AuditPtr            auditPtr;
  38.  * and initialize the driver log (in the driver open routine) by calling
  39.  *        devCtlEnt->auditPtr = InitAudit(
  40.  *                        gestaltSelector,
  41.  *                        nEntries,
  42.  *                        initiallyEnabled,
  43.  *                        preserveFirstFlag
  44.  *                    );
  45.  *
  46.  * The intialization routine creates an audit record in the System Heap and
  47.  * defines a gestalt selector with a pointer to the audit area. Once created,
  48.  * the record remains in the heap until the Macintosh is rebooted. The
  49.  * gestaltSelector should be unique to the driver. InitAudit need only be
  50.  * called once by any code segment that can allocate memory (i.e., by an
  51.  * application, INIT, or driver open function). Note that you do not
  52.  * need to check errors: if InitAudit fails to create (or locate) the record,
  53.  * it returns NULL and all other routines, upon receiving a NULL AuditPtr
  54.  * argument, do nothing gracefully.
  55.  *
  56.  * The log record contains a fixed header with the following information:
  57.  *
  58.  *    version.u.low        The earliest version of the AuditRecord that this instance
  59.  *                        of the library understands.
  60.  *    version.u.high        The latest version of the AuditRecord that this instance
  61.  *                        of the library understands.
  62.  *    size.auditRecord    The size of the Audit Record (excluding the dummy first
  63.  *                        entry). This protects against compiler alignment errors
  64.  *                        (which may occur if an Audit Record is created by code
  65.  *                        generated by one compiler, and accessed by code generated
  66.  *                        by a different compiler.
  67.  *    size.auditEntry        The size of the AuditQueueEntry. (See above.)
  68.  *    free.Queue            A standard O.S. queue with available data logging records.
  69.  *    data.queue            A standard O.S. queue with records waiting to be displayed
  70.  *                        or written to a file.
  71.  *    lostData            This counter is incremented when Audit function cannot
  72.  *                        obtain a record from the free queue.
  73.  *    PSN                    Awaken this process when something is stored.
  74.  *    refNum                a void * (longword) that may be used by the calling
  75.  *                        software.
  76.  *    isLogging            TRUE if logging is enabled.
  77.  *    timeAtStart            GetDateTime() when the log was created.
  78.  *    ticksAtStart        TickCount() when the log was created.
  79.  *    entries[]            A vector of AuditEntry records.
  80.  *
  81.  * The audit user accesses the log record by calling functions: it should not
  82.  * access the structure directly.
  83.  *
  84.  * Each log record is stored in a AuditEntry structure that contains the
  85.  * following information:
  86.  *    tickCount            The Ticks value at the time the data was collected.
  87.  *    lostData            This is the number of records that were "lost" before this
  88.  *                        record because there was no free entry in the queue.
  89.  *    idCode                This longword identifies the log entry (i.e. who logged it).
  90.  *    format                This longword describes the format of the log data.
  91.  *    data                This 32 byte data area contains the log. If format is zero,
  92.  *                        the data is a Str31. Otherwise, it contains up to 8
  93.  *                        (sizeof data / sizeof (long)) longwords of data.
  94.  *
  95.  * Your application or driver calls the following functions to manage the log:
  96.  *
  97.  *        AuditPtr            InitAudit(
  98.  *            OSType                gestaltSelector,
  99.  *            short                nEntries
  100.  *            Boolean                initiallyEnabled
  101.  *        );
  102.  *
  103.  *    Create the log record and "publish" a Gestalt selector with a pointer to the
  104.  *    record. This is normally called by a driver when it is first opened.
  105.  *    gestaltSelector is unique to the driver. InitAudit returns a pointer to the
  106.  *    log record or NULL if it could not create the log. If the gestaltSelector was
  107.  *    already registered, it returns a pointer to the already-created log without
  108.  *    creating a new log.
  109.  *
  110.  *        void                Audit(
  111.  *            AuditPtr            auditPtr,
  112.  *            OSType                idCode,
  113.  *            unsigned long        format,
  114.  *            ...
  115.  *        );
  116.  *
  117.  *    Store data in the log. idCode identifies this log request: by convention, it
  118.  *    is an OSType. The format parameter simplifies display by defining the format
  119.  *    of the subsequent data, The remaining parameters are log dependent. If auditPtr
  120.  *    is NULL or logging disabled, the function does nothing, but does not fail.
  121.  *
  122.  *    The AuditFormat macro is used to specify the format of the stored data:
  123.  *
  124.  *        AuditFormat(arg1, arg2, ..., arg8)
  125.  *
  126.  *    where arg1, etc. is specified by one of the following constants:
  127.  *        kAuditFormatEnd            No more data
  128.  *        kAuditFormatSigned        The datum is a 32-bit signed decimal integer
  129.  *        kAuditFormatUnsigned    The datum is a 32-bit unsigned decimal integer
  130.  *        kAuditFormatHex            The datum is a 32-bit address or 4-byte character
  131.  *        kAuditFormatAddress        The datum is a 32-bit hex value (never characters)
  132.  *        kAuditFormatString        The datum is a Pascal string. This must be the last
  133.  *                                (or only) argument. The string will be truncated
  134.  *                                to fit the remaining space. I.e. if it is the only
  135.  *                                argument, a 31-byte (plus one byte count) string
  136.  *                                will be stored.
  137.  *        kAuditFormatLocation    This must be the last format, but there is no
  138.  *                                associated value. Instead, the library stores
  139.  *                                the calling function name (the MacsBug name).
  140.  *                                The name will be truncated as for kAuditFormatString.
  141.  *
  142.  * Note that AuditFormat1, AuditFormat2, etc. macros are provided to simplify
  143.  * access to AuditFormat().
  144.  *
  145.  * Important: all parameters to Audit must be longwords. Short values (such as
  146.  * Booleans and OSErr codes) must be cast to long or unsigned long:
  147.  *        Audit(auditPtr, 'Err!', AuditFormat1(kAuditFormatSigned),
  148.  *            (long) statusError);
  149.  * If you do not do this, the data may be logged incorrectly or, in extreme
  150.  * cases, your program may crash.
  151.  *
  152.  ***
  153.  *        Boolean                AuditRead(
  154.  *            AuditPtr            auditPtr,
  155.  *            AuditEntry            *thisAuditEntry
  156.  *        );
  157.  *    An application program calls AuditRead periodically to retrieve data from the
  158.  *    log.  If there is a log entry to process, it is copied to thisAuditEntry and
  159.  *    the function returns TRUE. If it returns FALSE, there is no data waiting. This
  160.  *    function should be called from the log application's event loop. AuditRead
  161.  *    manages all log queues. Note that reading the log resets the missedDataCount.
  162.  *
  163.  * Each log record is time-stamped by the following algorithm:
  164.  *        GetAuditStartTimes(auditPtr, &gTimeAtStart, &gTicksAtStart);
  165.  *        if (AuditRead(auditPtr, &thisAuditEntry)) {
  166.  *            elapsedTicks = thisAuditEntry.tickCount - gTicksAtStart;
  167.  *            SecsToDate(
  168.  *                gTimeAtStart + (elapsedTicks / 60),
  169.  *                &logEntryDateString
  170.  *            );
  171.  *            printf(, ..., date.second, elapsedTicks % 60);
  172.  *        }
  173.  * See AuditEntryFormat.c for details.
  174.  *
  175.  ***
  176.  *        AuditPtr            GetAuditPtr(
  177.  *            OSType                gestaltSelector
  178.  *        );
  179.  *
  180.  *    Return a pointer to the common storage area, if it exists. This returns NULL
  181.  *    if there is no log "registered" by that name.
  182.  *
  183.  ***
  184.  *        void                WakeUpAudit(
  185.  *            AuditPtr            auditPtr,
  186.  *            ProcessSerialNumber    *oldPSN
  187.  *        );
  188.  *
  189.  *    Wake up this process (application) when something is logged: called by:
  190.  *            GetCurrentProcess((&oldPSN);
  191.  *            WakeUpAudit(auditPtr, &oldPSN);
  192.  *            ... logging stuff ...
  193.  *            WakeUpAudit(auditPtr, &oldPSN);    // restore previous
  194.  *            ExitToShell();
  195.  *
  196.  ***
  197.  *        Boolean                EnableAudit(
  198.  *            AuditPtr            auditPtr,
  199.  *            Boolean                enableLogging
  200.  *        );
  201.  *
  202.  *    Enable or disable logging. This returns the previous logging value. At
  203.  *    initialization, a compile-time parameter sets the initial logging value.
  204.  *
  205.  ***
  206.  *        Boolean                IsAuditEnabled(
  207.  *            AuditPtr            auditPtr
  208.  *        );
  209.  *
  210.  *    Return TRUE if auditing is enabled for this audit record.
  211.  *
  212.  ***
  213.  *        void                GetAuditStartTimes(
  214.  *            AuditPtr            auditPtr,
  215.  *            unsigned long        *timeAtStart,
  216.  *            unsigned long        *ticksAtStart
  217.  *        );
  218.  *    Return the times that the log was created. Using these values, the log display
  219.  *    application can time-stamp all log entries.
  220.  *
  221.  ***
  222.  *        void                SetAuditRefNum(
  223.  *            AuditPtr            auditPtr,
  224.  *            void                *refNum
  225.  *        );
  226.  * Store a user-controlled reference value. This may be coerced to any scalar
  227.  *    value (such as a memory pointer or longword).
  228.  *
  229.  ***
  230.  *        void                *GetAuditRefNum(
  231.  *            AuditPtr            auditPtr
  232.  *        );
  233.  * Return the current user-controlled reference value. This may be coerced to any
  234.  * scalar value (such as a memory pointer or longword). This returns zero if
  235.  * auditPtr is NULL or no value had been stored.
  236.  */
  237.  
  238. #include "Audit.h"
  239. #include <Types.h>
  240. #include <Traps.h>
  241. #include <Errors.h>
  242. #include <Memory.h>
  243. #include <GestaltEqu.h>
  244. #include <OSUtils.h>
  245. #ifndef PARANOIA
  246. #define PARANOIA    0
  247. #endif
  248.  
  249. /*
  250.  * The version id's are defined as (majorVersion << 8) | minorVersion
  251.  */
  252. #define kAuditEarliestVersion    ((1 << 8) | 1)
  253. #define kAuditLatestVersion        ((1 << 8) | 1)
  254.  
  255. #ifndef TRUE
  256. #define TRUE    1
  257. #define FALSE    0
  258. #endif
  259. /*
  260.  * This is Think C specific, and possibly release dependent.
  261.  */
  262. #if defined(_Gestalt) == FALSE && defined(_GestaltDispatch)
  263. #define _Gestalt    _GestaltDispatch
  264. #endif
  265.  
  266. /*
  267.  * Note that this file contains 68000 assembly. This must be modified when
  268.  * the Audit Library is converted to Power PC.
  269.  */
  270. /*
  271.  * These values must match values in AuditDCMD.c
  272.  */
  273. #define kAuditMagicHeaderSize    7            /* Size in longwords            */
  274. #define kAuditMagicHeader_0        0x4E560000    /* First word in Gestalt result    */
  275.  
  276. #if 0
  277. /*
  278.  * The following sequence was used to create the GestaltSelector stub. It is
  279.  * never compiled directly, but was pasted into a temporary file which was
  280.  * decompiled to get the machine code. 
  281.  */
  282. static pascal OSErr    GestaltSelector(
  283.         OSType            selector,
  284.         long            *result
  285.     )
  286. {
  287.         asm {
  288.             /*
  289.              * 22 was determined by painful observation and many pleasant
  290.              * encounters with MacsBug.
  291.              */
  292.             lea            22(pc),a0        /* a0 = Address of auditRecord        */
  293.             movea.l        result,a1        /* a1 -> result                        */
  294.             move.l        a0,(a1)            /* Store result                        */
  295.         }
  296.         return (noErr);
  297. }
  298. #endif
  299.  
  300. /*
  301.  * DisableInterrupts and EnableInterrupts manipulate the 68000 status register.
  302.  * They are expanded inline. A comment consisting of *** in the left margin
  303.  * indicates code that is run with interrupts disabled.
  304.  *
  305.  * unsigned short DisableInterrupts(void) {
  306.  *        asm {
  307.  *            move        sr,d0
  308.  *            move        #0x2600,sr
  309.  *        }
  310.  */
  311. unsigned short DisableInterrupts(void) = { 0x40C0, 0x46FC, 0x2600 };
  312. #ifdef THINK_C
  313. /*
  314.  * Since Think C has a real inline assembler, we can save one or two instructions.
  315.  * Nerd fun.
  316.  */
  317. #define EnableInterrupts(saveSR) asm {            \
  318.             move        saveSR,sr                \
  319.         }
  320. #else
  321. /*
  322.  * Even though the saveSR argument is a short, we must declare the function as a
  323.  * long so that both Think and MPW generate the same code. Otherwise, Think pushes
  324.  * a short and 2(sp) addresses the wrong value. (Guess how I discovered this.)
  325.  * Using the Think C inline defined above also eliminates the problem.
  326.  *
  327.  * void EnableInterrupts(unsigned long saveSR) {
  328.  *        asm {
  329.  *            move        2(sp),sr
  330.  *        }
  331.  */
  332. void EnableInterrupts(unsigned long saveSR) = { 0x46EF, 0x0002 };
  333. #endif
  334.  
  335. /*
  336.  * This inline copies the value of A6 (the frame pointer) to D0. It is needed in
  337.  * order to get the caller's function name.
  338.  */
  339. unsigned long *GetA6(void) = { 0x200E };    /* move.l A6,D0    */
  340.  
  341. #define LOG        (*auditPtr)
  342. #define ENTRY    (entryPtr->theEntry)
  343.  
  344. static void                            StoreString(
  345.         const StringPtr        theString,        /* Source, length may be wrong    */
  346.         unsigned short        stringLength,    /* Purported actual length        */
  347.         unsigned short        maxLength,        /* Maximum destination length    */
  348.         StringPtr            result            /* Destination pointer            */
  349.     );
  350. static AuditQueueEntryPtr            GetQueueEntry(
  351.         QHdrPtr                    theQueue
  352.     );
  353. #define PutQueueEntry(theQueue, theEntry) do {                \
  354.         Enqueue((QElemPtr) &theEntry->qLink, theQueue);        \
  355.     } while (0)
  356.  
  357. static Boolean                        TrapAvailable(
  358.         short                    theTrap
  359.     );
  360. static void                            GetCallerName(
  361.         unsigned long            *destPtr,
  362.         unsigned short            maxString,
  363.         unsigned long            *a6
  364.     );
  365.  
  366. /*
  367.  * InitAudit
  368.  */
  369. AuditPtr
  370. InitAudit(
  371.         OSType                    gestaltSelector,
  372.         unsigned short            nEntries,
  373.         Boolean                    initiallyEnabled,
  374.         Boolean                    preserveFirst
  375.     )
  376. {
  377.         register unsigned long        *recordPtr;
  378.         register AuditPtr            auditPtr;
  379.         register AuditQueueEntryPtr    queueEntryPtr;
  380.         Size                        logRecordSize;
  381.         short                        i;
  382.         OSErr                        status;
  383.         
  384.         /*
  385.          * This will not compile on either Think or MPW if the sizes are correct.
  386.          * Unfortunately, sizeof cannot be incorporated into a #define, so we
  387.          * have to do this at execution time.
  388.          */
  389.         if (sizeof (AuditEntry) != kSizeofAuditEntry)
  390.             DebugStr("\pAuditEntry size error");
  391.         if (sizeof (AuditQueueEntry) != kSizeofAuditQueueEntry)
  392.             DebugStr("\pAuditQueueEntry size error");
  393.         if (sizeof (AuditRecord) != kSizeofAuditRecord)
  394.             DebugStr("\pAuditRecord size error");
  395.         /*
  396.          * Presuppose problems and exit if the system is so old that it doesn't
  397.          * even support Gestalt. This is slightly overkill, as the current
  398.          * Gestalt glue does the TrapAvailable check for us.
  399.          */
  400.         auditPtr = NULL;
  401.         if (TrapAvailable(_Gestalt) == FALSE)
  402.             goto exit;
  403.         logRecordSize = sizeof (AuditRecord)
  404.                     + (kAuditMagicHeaderSize * sizeof (long))
  405.                     + ((nEntries - 1) * sizeof (AuditQueueEntry));
  406. #if PARANOIA > 1
  407.         {
  408.             Str255                    foo;
  409.             NumToString(sizeof (AuditQueueEntry), foo);
  410.             DebugStr(foo);
  411.             NumToString(logRecordSize, foo);
  412.             DebugStr(foo);
  413.         }
  414. #endif
  415.         recordPtr = (unsigned long *) NewPtrSysClear(logRecordSize);
  416.         if (recordPtr == NULL)
  417.             goto exit;
  418.         auditPtr = (AuditPtr) &recordPtr[kAuditMagicHeaderSize];
  419.         /*
  420.          * The code for the Gestalt Selector function precedes the actual audit
  421.          * record. Gestalt calls this function when a program calls GetAuditPtr
  422.          * for our selector. The function returns the address of the AuditRecord
  423.          * as the selector value. This code will need revision when run on
  424.          * non-Motorola cpu's. See the definition of GestaltSelector above for
  425.          * the original code.
  426.          *
  427.          * asm {
  428.          *        link        a6,#0000    Create stack frame    0x4E56 0x0000
  429.          *        lea            22(pc),a0    a0 -> auditRecord    0x41FA 0x0016  
  430.          *        movea.l        8(a6),a1    a1 -> response        0x226E 0x0008
  431.          *        move.l        a0,(a1)        Store result        0x2288
  432.          *        clr.w        8(a6)        Result := noErr        0x426E 0x0010
  433.          *        unlk        a6            Delete stack frame    0x4E5E
  434.          *        movea.l        (a7)+,a0    Pop return address    0x205F
  435.          *        addq.l        #0x8,a7        Clear stack params    0x508F
  436.          *        jmp            (a0)        Return to caller    0x4ED0
  437.          *        dc.w        0            Fake MacsBug name    0x0000
  438.          *        ** AuditRecord starts here **
  439.          *    };
  440.          */
  441. #if kAuditMagicHeader_0 != 0x4E560000
  442.     << error, the following won't work: see AuditDCMD.c >>
  443. #endif
  444.         recordPtr[0] = 0x4E560000;
  445.         recordPtr[1] = 0x41FA0016;
  446.         recordPtr[2] = 0x226E0008;
  447.         recordPtr[3] = 0x2288426E;
  448.         recordPtr[4] = 0x00104E5E;
  449.         recordPtr[5] = 0x205F508F;
  450.         recordPtr[6] = 0x4ED00000;        /* Pad with fake MacsBug name    */
  451. #if kAuditMagicHeaderSize != 7
  452.     << error, the above won't work: see AuditDCMD.c >>
  453. #endif
  454.         /*
  455.          * If this machine has a data/instruction cache, flush it so that we
  456.          * cannot execute stale data.
  457.          */
  458.         if (TrapAvailable(_HWPriv)) {
  459.             FlushDataCache();
  460.             FlushInstructionCache();
  461.         }
  462.         /*
  463.          * Add this as a gestalt. Errors present problems: either this was already
  464.          * added (no big deal), or the luser is trying to redefine a "normal"
  465.          * Gestalt. We check by trying to get the AuditPtr and hope that
  466.          * GetAuditPtr's internal checks ferret out an attempt to redefine a
  467.          * normal Gestalt.
  468.          */
  469.         status = NewGestalt(gestaltSelector, (ProcPtr) recordPtr);
  470.         if (status == gestaltDupSelectorErr) {
  471.             /*
  472.              * Trouble! this has already been added! Your program
  473.              * may eventually crash if the gestalt is not an audit
  474.              * record: see GetAuditPtr() for the safety checks.
  475.              */
  476.             DisposPtr((Ptr) auditPtr);
  477.             auditPtr = GetAuditPtr(gestaltSelector);
  478.             goto exit;
  479.         }
  480.         else if (status != noErr) {
  481.             /*
  482.              * Something is seriously out of order. Just return NULL.
  483.              */
  484.             DisposPtr((Ptr) recordPtr);
  485.             auditPtr = NULL;
  486.             goto exit;
  487.         }
  488.         /*
  489.          * Setup the rest of the AuditRecord.
  490.          */
  491.         LOG.version.u.low = kAuditEarliestVersion;
  492.         LOG.version.u.high = kAuditLatestVersion;
  493.         LOG.recordSize = kAuditRecordSize;
  494.         GetDateTime(&LOG.timeAtStart);
  495.         LOG.ticksAtStart = TickCount();
  496.         LOG.PSN.lowLongOfPSN = kNoProcess;
  497.         /*
  498.          * Ok, so far. Now build the initial free queue.
  499.          */
  500.         queueEntryPtr = &LOG.entries[0];
  501.         for (i = 0; i < nEntries; i++) {
  502.             PutQueueEntry(&LOG.free.queue, queueEntryPtr);
  503.             ++queueEntryPtr;
  504.         }
  505. exit:    EnableAudit(auditPtr, initiallyEnabled);
  506.         PreserveAudit(auditPtr, preserveFirst);
  507.         return (auditPtr);
  508. }
  509.  
  510. /*
  511.  * This function logs data if logging is enabled.
  512.  *
  513.  *        auditPtr    As returned by InitAudit. If NULL, nothing is logged.
  514.  *        idCode        A user-controlled value, by convention an OSType (4-byte
  515.  *                    character), that identifies this entry. The display program
  516.  *                    prints it.
  517.  *        format        The format of the remaining data: kAuditFormatString if the
  518.  *                    datum is a pascal string, otherwise, it is as created by the
  519.  *                    AuditFormat macro.
  520.  *        ...            Additional data as needed. These values must be cast to
  521.  *                    longwords to prevent ThinkC/MPW incompatibilities.
  522.  */
  523. void
  524. Audit(
  525.         register AuditPtr        auditPtr,
  526.         OSType                    idCode,
  527.         unsigned long            format,
  528.         ...
  529.     )
  530. {
  531.         unsigned short                saveSR;
  532.         va_list                        argPtr;
  533.         register unsigned long        *destPtr;
  534.         unsigned long                tickCount;
  535.         unsigned short                maxString;
  536.         unsigned short                thisFormat;
  537.         StringPtr                    theString;
  538.         register AuditQueueEntryPtr    entryPtr;
  539.         register AuditQueueEntryPtr    nextPtr;
  540.  
  541.         tickCount = TickCount();
  542.         if (auditPtr != NULL && (LOG.flags & kAuditEnabledMask) != 0) {
  543.             entryPtr = GetQueueEntry(&LOG.free.queue);
  544.             /*
  545.              * Note: *** in the left-most column indicates code that is executed
  546.              * with interrupts disabled.
  547.              */
  548.             saveSR = DisableInterrupts();
  549. /***/        if (entryPtr == NULL) {
  550. /***/            ++LOG.lostData;
  551. /***/            EnableInterrupts(saveSR);
  552.                 if ((LOG.flags & kAuditPreserveFirstMask) == 0) {
  553.                     /*
  554.                      * We really want to log the latest entry. Remove the first
  555.                      * "to be displayed" entry. If successful, it will get the new
  556.                      * record and the next "to be displayed" entry, if any, gets
  557.                      * the lost data counter.
  558.                      */
  559.                     entryPtr = GetQueueEntry(&LOG.data.queue);
  560.                     if (entryPtr == NULL)
  561.                         ;                /* goto return        */
  562.                     else {
  563. /***/                    saveSR = DisableInterrupts();
  564. /***/                    LOG.lostData += ENTRY.lostData;
  565. /***/                    nextPtr = (AuditQueueEntryPtr) LOG.data.queue.qHead;
  566. /***/                    if (nextPtr != NULL) {
  567. /***/                        nextPtr->theEntry.lostData += LOG.lostData;
  568. /***/                        LOG.lostData = 0;
  569. /***/                    }
  570. /***/                    goto returnThisEntry;
  571.                     }
  572.                 }
  573.             }
  574. /***/        else {
  575. returnThisEntry:
  576. /***/            ENTRY.lostData = LOG.lostData;
  577. /***/            LOG.lostData = 0;
  578. /***/            EnableInterrupts(saveSR);
  579.                 ENTRY.tickCount = tickCount;
  580.                 ENTRY.idCode = idCode;
  581.                 ENTRY.format = format;
  582.                 va_start(argPtr, format);
  583.                 destPtr = ENTRY.data;
  584.                 maxString = sizeof (ENTRY.data);
  585.                 while (maxString > 0) {
  586.                     thisFormat = format & kAuditFormatMask;
  587.                     switch (thisFormat) {
  588.                     case kAuditFormatEnd:
  589.                         goto exitLoop;
  590.                     case kAuditFormatString:
  591.                         theString = va_arg(argPtr, StringPtr);
  592.                         StoreString(
  593.                             theString,
  594.                             theString[0],
  595.                             maxString,
  596.                             (StringPtr) destPtr
  597.                         );
  598.                         goto exitLoop;
  599.                     case kAuditFormatLocation:
  600.                         GetCallerName(destPtr, maxString, GetA6());
  601.                         goto exitLoop;
  602.                     default:
  603.                         *destPtr++ = va_arg(argPtr, unsigned long);
  604.                         format >>= kAuditFormatShift;
  605.                         maxString -= sizeof (unsigned long);
  606.                         break;
  607.                     }
  608.                 }
  609. exitLoop:        va_end(nextArg);
  610.                 PutQueueEntry(&LOG.data.queue, entryPtr);
  611.                 if (LOG.PSN.highLongOfPSN != 0
  612.                  || LOG.PSN.lowLongOfPSN != kNoProcess)
  613.                     (void) WakeUpProcess(&LOG.PSN);
  614.             }
  615.         }
  616. #undef DATA
  617. }
  618.  
  619. /*
  620.  * The following functions are normally called by the display application:
  621.  */
  622.  
  623. /*
  624.  * Return a pointer to the log area, or NULL if there is none. Note that we make
  625.  * a few sanity checks to better ensure that the gestaltSelector actually refers
  626.  * to a Audit record.
  627.  */
  628. AuditPtr
  629. GetAuditPtr(
  630.         OSType                    gestaltSelector
  631.     )
  632. {
  633.         auto long                gestaltResponse;
  634.         register AuditPtr        auditPtr;
  635.  
  636.         if (TrapAvailable(_Gestalt) == FALSE
  637.          || Gestalt(gestaltSelector, &gestaltResponse) != noErr
  638.          || gestaltResponse == 0
  639.          || (gestaltResponse & 0x3) != 0)
  640.              auditPtr = NULL;
  641.         else {
  642.             auditPtr = (AuditPtr) gestaltResponse;
  643.             /*
  644.              * Sanity check: make sure the result is really an Audit Record and
  645.              * we can deal with the library release that created it.
  646.              */
  647.             if (((unsigned long *) auditPtr)[-kAuditMagicHeaderSize]
  648.                     != kAuditMagicHeader_0
  649.              || LOG.version.u.low < kAuditEarliestVersion
  650.              || LOG.version.u.high > kAuditLatestVersion
  651.              || LOG.recordSize != kAuditRecordSize)
  652.                 auditPtr = NULL;
  653.         }
  654.         return (auditPtr);
  655. }
  656.  
  657. /*
  658.  * Awaken a specified process. Call by the following sequence:
  659.  *        GetCurrentProcess(&oldPSN);        // process to awaken
  660.  *        WakeUpAudit(auditPtr, &oldPSN);
  661.  *    The previous process is now stored in oldPSN)
  662.  *        ... display the log ...
  663.  *        WakeUpAudit(auditPtr, &oldPSN);    // restore old
  664.  *        ExitToShell();
  665.  */
  666. void
  667. WakeUpAudit(
  668.         register AuditPtr        auditPtr,
  669.         ProcessSerialNumber        *oldPSN
  670.     )
  671. {
  672.         ProcessSerialNumber        previousPSN;
  673.         short                    saveSR;
  674.         long                    gestaltResult;
  675.         OSErr                    status;
  676.         
  677.         if (auditPtr != NULL) {
  678.             status = Gestalt(gestaltOSAttr, &gestaltResult);
  679.             if (status != noErr
  680.              || (gestaltResult & (1 << gestaltLaunchControl)) == 0) {
  681.                 oldPSN->highLongOfPSN = 0;
  682.                 oldPSN->lowLongOfPSN = kNoProcess;
  683.             }
  684. /***/        saveSR = DisableInterrupts();
  685. /***/        previousPSN = LOG.PSN;
  686. /***/        LOG.PSN = *oldPSN;
  687. /***/        EnableInterrupts(saveSR);
  688.             *oldPSN = previousPSN;
  689.         }
  690. }
  691.  
  692. /*
  693.  * Enable/disable logging. Returns the old logging state.
  694.  */
  695. Boolean
  696. EnableAudit(
  697.         register AuditPtr        auditPtr,
  698.         Boolean                    enableLogging
  699.     )
  700. {
  701.         Boolean                    oldLogState;
  702.         short                    saveSR;
  703.         
  704.         if (auditPtr == NULL)
  705.             oldLogState = FALSE;
  706.         else {
  707. /***/        saveSR = DisableInterrupts();
  708. /***/        oldLogState = (LOG.flags & kAuditEnabledMask) != 0;
  709. /***/        LOG.flags &= ~kAuditEnabledMask;
  710. /***/        if (enableLogging)
  711. /***/            LOG.flags |= kAuditEnabledMask;
  712. /***/        EnableInterrupts(saveSR);
  713.         }
  714.         return (oldLogState);
  715. }
  716.  
  717. /*
  718.  * Return the value of the Audit enable flag. Returns FALSE if auditPtr is NULL
  719.  * or auditing is disabled.
  720.  */
  721. Boolean
  722. IsAuditEnabled(
  723.         AuditPtr                auditPtr
  724.     )
  725. {
  726.         if (auditPtr == NULL)
  727.             return (FALSE);
  728.         else {
  729.             return ((LOG.flags & kAuditEnabledMask) != 0);
  730.         }
  731. }
  732.  
  733. /*
  734.  * Enable/disable logging. Returns the old logging state.
  735.  */
  736. Boolean
  737. PreserveAudit(
  738.         register AuditPtr        auditPtr,
  739.         Boolean                    preserveFirst
  740.     )
  741. {
  742.         Boolean                    oldPreserveState;
  743.         short                    saveSR;
  744.         
  745.         if (auditPtr == NULL)
  746.             oldPreserveState = FALSE;
  747.         else {
  748. /***/        saveSR = DisableInterrupts();
  749. /***/        oldPreserveState = (LOG.flags & kAuditPreserveFirstMask) != 0;
  750. /***/        LOG.flags &= ~kAuditPreserveFirstMask;
  751. /***/        if (preserveFirst)
  752. /***/            LOG.flags |= kAuditPreserveFirstMask;
  753. /***/        EnableInterrupts(saveSR);
  754.         }
  755.         return (oldPreserveState);
  756. }
  757.  
  758. /*
  759.  * Get the time that the log record was created. This is used to time-stamp log
  760.  * entries. Since the time-stamp is created only when the log is created (and
  761.  * before it is visible through Gestalt), we don't need to lock out interrupts.
  762.  */
  763. void
  764. GetAuditStartTimes(
  765.         register AuditPtr        auditPtr,
  766.         unsigned long            *timeAtStart,
  767.         unsigned long            *ticksAtStart
  768.     )
  769. {
  770.         if (auditPtr == NULL) {
  771.             *timeAtStart = 0;
  772.             *ticksAtStart = 0;
  773.         }
  774.         else {
  775.             *timeAtStart = LOG.timeAtStart;
  776.             *ticksAtStart = LOG.ticksAtStart;
  777.         }
  778. }
  779.  
  780. /*
  781.  * Read the next log entry. This returns a copy of the log entry, if one is
  782.  * available and returns TRUE. This function manages all log queues. If no entry
  783.  * is available, or logging is disabled, the function returns FALSE.
  784.  */
  785. Boolean
  786. ReadAudit(
  787.         register AuditPtr        auditPtr,
  788.         AuditEntryPtr            thisAuditEntry
  789.     )
  790. {
  791.         register AuditQueueEntryPtr    entryPtr;
  792.         
  793.         if (auditPtr == NULL)
  794.             return (FALSE);
  795.         else {
  796.             entryPtr = GetQueueEntry(&LOG.data.queue);
  797.             if (entryPtr == NULL)
  798.                 return (FALSE);
  799.             else {
  800.                 *thisAuditEntry = ENTRY;
  801.                 PutQueueEntry(&LOG.free.queue, entryPtr);
  802.                 return (TRUE);
  803.             }
  804.         }
  805. }
  806.  
  807. /*
  808.  * Store a user-controlled reference value. This may be coerced to any scalar
  809.  * value (such as a memory pointer or longword). SetAuditRefNum returns the
  810.  * current value of the refNum, or zero if auditPtr is NULL.
  811.  */
  812. void
  813. *SetAuditRefNum(
  814.         AuditPtr                auditPtr,
  815.         void                    *refNum
  816.     )
  817. {
  818.         void                *result;
  819.         short                    saveSR;
  820.         
  821.         if (auditPtr == NULL)
  822.             result = NULL;
  823.         else {
  824. /***/        saveSR = DisableInterrupts();
  825. /***/        result = LOG.refNum;
  826. /***/        LOG.refNum = refNum;
  827. /***/        EnableInterrupts(saveSR);
  828.         }
  829.         return (result);
  830. }
  831.  
  832. /*
  833.  * Return the current user-controlled reference value. This may be coerced to any
  834.  * scalar value (such as a memory pointer or longword). This returns zero if
  835.  * auditPtr is NULL or no value had been stored.
  836.  */
  837. void *
  838. GetAuditRefNum(
  839.         AuditPtr                auditPtr
  840.     )
  841. {
  842.         /*
  843.          * Note that we assume that we can retrieve LOG.refNum without
  844.          * interference from interrupt routines. Is this reasonable?
  845.          */
  846.         return ((auditPtr == NULL) ? NULL : LOG.refNum);
  847. }
  848.  
  849. /*
  850.  * Remove the first entry from the queue, return NULL on failure.
  851.  */
  852. static AuditQueueEntryPtr
  853. GetQueueEntry(
  854.         QHdrPtr                    theQueue
  855.     )
  856. {
  857.         register QElemPtr        qElemPtr;
  858.         
  859.         if ((qElemPtr = theQueue->qHead) != NULL) {
  860.             if (Dequeue(qElemPtr, theQueue) != noErr)
  861.                 qElemPtr = NULL;
  862.         }
  863.         return ((AuditQueueEntryPtr) qElemPtr);
  864. }
  865.  
  866. /*
  867.  * TrapAvailable (see Inside Mac VI 3-8)
  868.  */
  869. #define NumToolboxTraps() (                                \
  870.         (NGetTrapAddress(_InitGraf, ToolTrap)            \
  871.                 == NGetTrapAddress(0xAA6E, ToolTrap))    \
  872.             ? 0x200 : 0x400                                \
  873.     )
  874. #define GetTrapType(theTrap) (                            \
  875.         ((theTrap) & 0x800 != 0) ? ToolTrap : OSTrap    \
  876.     )
  877.  
  878. static Boolean
  879. TrapAvailable(
  880.         short                    theTrap
  881.     )
  882. {
  883.         TrapType                trapType;
  884.         
  885.         trapType = GetTrapType(theTrap);
  886.         if (trapType == ToolTrap) {
  887.             theTrap &= 0x07FF;
  888.             if (theTrap >= NumToolboxTraps())
  889.                 theTrap = _Unimplemented;
  890.         }
  891.         return (
  892.             NGetTrapAddress(theTrap, trapType)
  893.             != NGetTrapAddress(_Unimplemented, ToolTrap)
  894.         );
  895. }
  896.  
  897. /*
  898.  * GetCallerName peeks up the calling chain list to the Audit function caller. It
  899.  * then scans through the function code to locate the start and end of the
  900.  * function. The end of the function is followed by the MacsBug name (maybe).
  901.  * This is based on ShowStackChain.c by Greg Anderson greggor@apple.com
  902.  */
  903. static void
  904. GetCallerName(
  905.         unsigned long            *destPtr,
  906.         unsigned short            maxString,
  907.         unsigned long            *a6
  908.     )
  909. {
  910.         unsigned long                returnAddress;
  911.         register unsigned short        *pc;
  912.         short                        maxSearch;
  913.         unsigned short                length;
  914.         Str31                        tempString;
  915.         unsigned long                pcOffset;
  916. #define pcByte        ((unsigned char *) pc)
  917.  
  918.         returnAddress = a6[1];            /* Return address to Audit caller    */
  919.         /*
  920.          * First, look forwards for the symbol name
  921.          */
  922.         pc = (unsigned short *) returnAddress;
  923.         tempString[0] = 0;
  924.         for (maxSearch = 0; maxSearch < 0x4000; maxSearch++, pc++) {
  925.             if (pc[0] == 0x4E56)        /* link                                */
  926.                 break;                    /* We've gone too far                */
  927.             if (pc[0] == 0x4E75            /* rts                                */
  928.               || pc[0] == 0x4ED0        /* jmp (a0)                            */
  929.               || pc[0] == 0x4E74) {        /* rtd #N                            */
  930.                   if (pc[0] == 0x4E74)    /* If rtd, skip over displacement    */
  931.                     pc++;
  932.                 pc++;                    /* Skip over return instruction        */
  933.                  if (pc[0] != 0x4E56) {    /* Link instruction means no name    */
  934.                      /*
  935.                       * pc now points to the word following the RTS. Use the
  936.                       * MacsBug naming conventions to determine the function name,
  937.                       * if any. See Appendix D in the MacsBug User's Guide.
  938.                       */
  939.                       length = pcByte[0] & 0x7F;
  940.                       if (length >= 0x20 && length <= 0x7F) {
  941.                          /*
  942.                           * Fixed length format: first byte is in the range 0x20
  943.                           * through 0x7F, the high bit may or may not be set.
  944.                           */
  945.                           if ((pcByte[1] & 0x80) != 0) {
  946.                               /*
  947.                                * Pascal 16-byte class name format. The string is
  948.                                * stored "method" "class" (each takes 8 bytes) --
  949.                                * MacsBug swaps the order and inserts a '.'
  950.                                * (Warning, this is untested.)
  951.                                */
  952.                               BlockMove(&pcByte[0], &tempString[10], 8);
  953.                               BlockMove(&pcByte[7], &tempString[1], 8);
  954.                               tempString[10] &= ~0x80;
  955.                               tempString[11] &= ~0x80;
  956.                               tempString[9] = '.';
  957.                               tempString[0] = 17;
  958.                           }
  959.                           else {
  960.                               /*
  961.                                * Pascal 8 byte format.
  962.                                */
  963.                               BlockMove(&pcByte[0], &tempString[1], 8);
  964.                               tempString[0] = 7;
  965.                           }
  966.                     }
  967.                     else if (pcByte[0] >= 0x80 && pcByte[0] <= 0x9F) {
  968.                         /*
  969.                          * Variable-length string format. If the length byte is
  970.                          * zero after removing the flag bit, the next byte has
  971.                          * the true (Str255) length.
  972.                          */
  973.                         if (length == 0) {
  974.                             /*
  975.                              * Step over the flag. Note that pc and pcByte are the
  976.                              * same variable (pc is a "short *" while pcByte is a
  977.                              * "char *"). The next statement adds 1 to pc in a way
  978.                              * that doesn't cause ANSI compilers to lose their
  979.                              * electronic lunches.
  980.                              */
  981.                             pc = (unsigned short *) &pcByte[1];
  982.                             length = pcByte[0];
  983.                         }
  984.                         StoreString(
  985.                             pcByte,
  986.                             length,
  987.                             sizeof (tempString) - 1,
  988.                             tempString
  989.                         );
  990.                     }
  991.                     else {
  992.                         /*
  993.                          * Something isn't understandable. Do nothing. This will
  994.                          * be caught by the "if there is no symbol" test below.
  995.                          */
  996.                     }
  997.                 }
  998.                 break;
  999.             }
  1000.         }
  1001.         /*
  1002.          * If there is no symbol name, don't search for an offset.
  1003.          */
  1004.         pcOffset = 0;
  1005.         if (tempString[0] != 0) {
  1006.             pc = (unsigned short *) returnAddress;
  1007.             for (maxSearch = 0; maxSearch < 0x4000; maxSearch++) {
  1008.                 if (pc[0] == 0x4E56) {                /* link instruction    */
  1009.                     pcOffset =
  1010.                         ((unsigned long) returnAddress)
  1011.                         - ((unsigned long) pc);
  1012.                     break;
  1013.                 }
  1014.                 --pc;
  1015.             }
  1016.         }
  1017.         if (pcOffset == 0 || tempString[0] == 0) {
  1018.             /*
  1019.              * Since we don't have a MacsBug name, set the offset value to zero
  1020.              * (this will act as a signal) and store  the return address in the
  1021.              * second data value.
  1022.              */
  1023.             if (maxString >= sizeof (unsigned long)) {
  1024.                 *destPtr++ = 0;
  1025.                 maxString -= sizeof (unsigned long);
  1026.             }
  1027.             if (maxString >= sizeof (unsigned long)) {
  1028.                 *destPtr++ = returnAddress;
  1029.                 maxString -= sizeof (unsigned long);
  1030.             }
  1031.         }
  1032.         else {
  1033.             if (maxString >= sizeof (unsigned long)) {
  1034.                 *destPtr++ = pcOffset;
  1035.                 maxString -= sizeof (unsigned long);
  1036.                 StoreString(
  1037.                     tempString,
  1038.                     tempString[0],
  1039.                     maxString,
  1040.                     (StringPtr) destPtr
  1041.                 );
  1042.             }
  1043.         }
  1044. #undef pcByte
  1045. }
  1046.  
  1047. /*
  1048.  * Copy the source string to the destination, respecting the maximum size.
  1049.  */
  1050. static void
  1051. StoreString(
  1052.         const StringPtr        theString,
  1053.         unsigned short        stringLength,
  1054.         unsigned short        maxLength,
  1055.         StringPtr            result
  1056.     )
  1057. {
  1058.         if (stringLength >= maxLength)
  1059.             stringLength = maxLength - 1;
  1060.         BlockMove(theString, result, stringLength + 1);
  1061.         result[0] = stringLength;
  1062. }
  1063.             
  1064.  
  1065.